package com.atguigu.gmall.cart.service.impl;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

import com.atguigu.gmall.cart.service.CartService;
import com.atguigu.gmall.common.constants.RedisConst;
import com.atguigu.gmall.common.execption.GmallException;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.common.result.ResultCodeEnum;
import com.atguigu.gmall.common.util.JsonUtil;
import com.atguigu.gmall.common.util.UserAuthToUtil;
import com.atguigu.gmall.feign.product.ProductFeignClient;
import com.atguigu.gmall.model.cart.CartItem;
import com.atguigu.gmall.model.product.SkuInfo;
import com.atguigu.gmall.model.to.UserAuthTo;
import com.fasterxml.jackson.core.type.TypeReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

/**
 * @author A
 * @version 1.0
 * @description: TODO
 * @date 2024/10/5 8:38
 */
@Service
public class CartServiceImpl implements CartService {
    @Autowired
    StringRedisTemplate redisTemplate;
    @Autowired
    ProductFeignClient productFeignClient;
    @Qualifier("otherPool")
    @Autowired
    ThreadPoolExecutor otherPool;

    @Override
    public CartItem addSkuToCart(Long skuId, Integer skuNum) {

        //判断用哪个key
        String cartKey = getCartKey();
        cartSizeIsOver(cartKey);
        //保存到redis
        CartItem cartItem = saveSkuToRedis(skuId, skuNum, cartKey);
        return cartItem;
    }

    private void cartSizeIsOver(String cartKey) {
        BoundHashOperations<String, Object, Object> cart = redisTemplate.boundHashOps(cartKey);
        if (cart.size() >= RedisConst.CART_SIZE_LIMIT) {
            throw new GmallException(ResultCodeEnum.CART_SIZE_OVER);
        }
    }

    //判断cartKey是哪种
    @Override
    public String getCartKey() {
        UserAuthTo userAuthTo = UserAuthToUtil.getUserAuthTo();
        if (userAuthTo.getUserId() != null) {

            //表明登录了
            return RedisConst.USER_CART_PREFIX + userAuthTo.getUserId();
        }
        if (userAuthTo.getUserTempId() != null) {
            //表明没登录用临时key
            return RedisConst.USER_CART_PREFIX + userAuthTo.getUserTempId();
        }
        return null;
    }

    //保存sku购物车项
    @Override
    public CartItem saveSkuToRedis(Long skuId, Integer skuNum, String cartKey) {
        //先判断是保存还是增加num
        //绑定一个购物车key的操作 key:key value:hash(skuId,Json)
        BoundHashOperations<String, String, String> cart = redisTemplate.boundHashOps(cartKey);
        //判断是否有skuId
        Boolean hasKey = cart.hasKey(skuId.toString());
        if (hasKey) {
            //有skuId表明已经有该项,数量增加
            String json = cart.get(skuId.toString());
            CartItem cartItem = JsonUtil.json2Obj(json, new TypeReference<CartItem>() {
            });
            cartItem.setSkuNum(cartItem.getSkuNum() + skuNum);
            cartItem.setUpdateTime(new Date());
            //保存到redis中
            cart.put(skuId.toString(), JsonUtil.obj2Json(cartItem));

            return cartItem;
        } else {
            //没有,没有该项,直接添加

            //远程调用拿到skuInfo
            Result<SkuInfo> skuInfoResult = productFeignClient.getSkuInfo(skuId);
            SkuInfo skuInfo = skuInfoResult.getData();
            CartItem cartItem = buildCartItem(skuInfo, skuNum);

            cart.put(skuId.toString(), JsonUtil.obj2Json(cartItem));
            return cartItem;
        }


    }

    //展示购物车列表
    @Override
    public List<CartItem> cartList() {
        //从redis中拿到指定user:cart:{userId} 为key的购物车
        String userCartKey = getUserCartKey();
        String tempCartKey = getTempCartKey();
        //先判断是否要购物车合并
        //拿到用户购物车和临时购物车
        BoundHashOperations<String, String, String> userCart = getUserCart();
        BoundHashOperations<String, String, String> tempUserCart = getTempUserCart();

        List<CartItem> mergeCart = new ArrayList<>();
        //判断用户购物车和临时购物车的存在情况
        if (userCart != null && tempUserCart != null && tempUserCart.size() > 0) {
            //用户购物车不为空;临时购物车不为空;合并购物车
            //合并购物车前判断是否超过上限
            if (userCart.size() + tempUserCart.size() >= RedisConst.CART_SIZE_LIMIT) {
                throw new GmallException(ResultCodeEnum.CART_MERGE_SIZE_OVER);
            }
            List<String> tempValues = tempUserCart.values();
            //合并购物车前判断合并后是否超过上限
            //将每项临时购物车合并到用户购物车
            tempValues.stream().map((tempValue) -> {
                CartItem tempItem = JsonUtil.json2Obj(tempValue, new TypeReference<CartItem>() {
                });
                return tempItem;
            }).forEach((tempItem) -> {
                CartItem cartItem = saveSkuToRedis(tempItem.getSkuId(), tempItem.getSkuNum(), userCartKey);
                mergeCart.add(cartItem);
            });
            //删除临时购物车
            deleteCartByKey(tempCartKey);
            syncGetPrice(userCartKey);
            return mergeCart;
        }
        //不用合并购物车,直接返回购物车(方法中自己判断了是哪种key)
        String cartKey = getCartKey();

        //拿到购物车列表集合
        List<CartItem> collect = getCart(cartKey);
        syncGetPrice(cartKey);
        return collect;
    }

    //异步查价格
    public void syncGetPrice(String cartKey) {
        otherPool.execute(() -> {
            List<CartItem> cartItemList = getCart(cartKey);
            cartItemList.stream().forEach((cartItem)->{
                //实时查到价格
                BigDecimal price = productFeignClient.getPrice(cartItem.getSkuId()).getData();
                //更新到redis中
                updateSkuItemPrice(cartKey,cartItem.getSkuId(),price);
            });
        });
    }
    //把实时价格更新到redis中
    @Override
    public void updateSkuItemPrice(String cartKey, Long skuId, BigDecimal price) {
        CartItem cartItem = getCartItem(skuId, cartKey);
        cartItem.setSkuPrice(price);
        //重新存储
        updateSkuToCart(skuId,cartKey,cartItem);
    }

    //根据key拿到购物车列表
    private List<CartItem> getCart(String cartKey) {
        BoundHashOperations<String, String, String> cart = redisTemplate.boundHashOps(cartKey);
        List<String> values = cart.values();
        List<CartItem> collect = values.stream().map((item) -> {
            return JsonUtil.json2Obj(item, new TypeReference<CartItem>() {
            });
        }).sorted((o1, o2) -> {
            return o2.getUpdateTime().compareTo(o1.getUpdateTime());
        }).collect(Collectors.toList());

        return collect;
    }


    //根据key删除购物车
    private void deleteCartByKey(String tempCartKey) {
        redisTemplate.delete(tempCartKey);
    }

    //用户购物车key
    private String getUserCartKey() {
        UserAuthTo userAuthTo = UserAuthToUtil.getUserAuthTo();
        Long userId = userAuthTo.getUserId();
        if (userId != null) {
            return RedisConst.USER_CART_PREFIX + userId;
        }
        return null;
    }

    //临时用户购物车key
    private String getTempCartKey() {
        UserAuthTo userAuthTo = UserAuthToUtil.getUserAuthTo();
        String userTempId = userAuthTo.getUserTempId();
        if (userTempId != null) {
            return RedisConst.USER_CART_PREFIX + userTempId;
        }
        return null;
    }

    //拿到用户购物车
    private BoundHashOperations<String, String, String> getUserCart() {
        UserAuthTo userAuthTo = UserAuthToUtil.getUserAuthTo();
        Long userId = userAuthTo.getUserId();
        if (userId != null) {
            BoundHashOperations<String, String, String> userCart = redisTemplate.boundHashOps(RedisConst.USER_CART_PREFIX + userId);
            return userCart;
        }

        return null;
    }

    //拿到临时用户购物车
    private BoundHashOperations<String, String, String> getTempUserCart() {
        UserAuthTo userAuthTo = UserAuthToUtil.getUserAuthTo();
        String userTempId = userAuthTo.getUserTempId();
        if (userTempId != null) {
            BoundHashOperations<String, String, String> userTempCart = redisTemplate.boundHashOps(RedisConst.USER_CART_PREFIX + userTempId);
            return userTempCart;
        }
        return null;
    }

    @Override
    public void updateNumToCart(Long skuId, Integer num) {
        String cartKey = getCartKey();
        CartItem cartItem = getCartItem(skuId, cartKey);
        cartItem.setSkuNum(cartItem.getSkuNum() + num);
        cartItem.setUpdateTime(new Date());
        //再存回redis中
        BoundHashOperations<String, String, String> cart = redisTemplate.boundHashOps(cartKey);
        cart.put(skuId.toString(), JsonUtil.obj2Json(cartItem));
    }

    //修改选中状态
    @Override
    public void updateCheckToCart(Long skuId, Integer checked) {
        String cartKey = getCartKey();
        CartItem cartItem = getCartItem(skuId, cartKey);
        cartItem.setIsChecked(checked);
        updateSkuToCart(skuId, cartKey, cartItem);

    }

    //删除购物车项
    @Override
    public void deleteCartItem(Long skuId) {
        String cartKey = getCartKey();
        BoundHashOperations<String, String, String> cart = redisTemplate.boundHashOps(cartKey);
        cart.delete(skuId.toString());
    }

    //删除选中购物车项
    @Override
    public void deleteChecked() {
        String cartKey = getCartKey();
        BoundHashOperations<String, String, String> cart = redisTemplate.boundHashOps(cartKey);
        List<String> values = cart.values();
        //拿到选中的skuId集合
        Object[] objects = values.stream().map((json) -> {
            CartItem cartItem = JsonUtil.json2Obj(json, new TypeReference<CartItem>() {
            });
            return cartItem;
        }).filter((cartItem) -> {
            return cartItem.getIsChecked() == 1;
        }).map((cartItem) -> {
            return cartItem.getSkuId().toString();
        }).toArray();
        if (objects != null && objects.length > 0) {
            cart.delete(objects);
        }

    }
    //获得选中的购物车项
    @Override
    public List<CartItem> getCartItemForOrderVo() {
        String cartKey = getCartKey();
        List<CartItem> cartItems = getCart(cartKey);
        //返回选中的购物车项列表
        List<CartItem> collect = cartItems.stream().filter((cartItem) -> {
            return cartItem.getIsChecked() == 1;
        }).collect(Collectors.toList());
        return collect;
    }

    //保存购物车项
    private void updateSkuToCart(Long skuId, String cartKey, CartItem cartItem) {
        BoundHashOperations<String, String, String> cart = redisTemplate.boundHashOps(cartKey);
        cart.put(skuId.toString(), JsonUtil.obj2Json(cartItem));
    }

    //得到某个skuId的购物车项
    private CartItem getCartItem(Long skuId, String cartKey) {
        BoundHashOperations<String, String, String> cart = redisTemplate.boundHashOps(cartKey);
        String json = cart.get(skuId.toString());
        CartItem cartItem = JsonUtil.json2Obj(json, new TypeReference<CartItem>() {
        });
        return cartItem;
    }

    private CartItem buildCartItem(SkuInfo skuInfo, Integer skuNum) {
        UserAuthTo userAuthTo = UserAuthToUtil.getUserAuthTo();
        Long userId = userAuthTo.getUserId();
        CartItem cartItem = new CartItem();
        if (userId != null) {
            //使用userId
            cartItem.setUserId(userId.toString());
        } else {
            //userId为空使用临时id
            cartItem.setUserId(userAuthTo.getUserTempId());
        }

        cartItem.setId(skuInfo.getId());

        cartItem.setSkuId(skuInfo.getId());
        cartItem.setCartPrice(skuInfo.getPrice());
        cartItem.setSkuNum(skuNum);
        cartItem.setSkuDefaultImg(skuInfo.getSkuDefaultImg());
        cartItem.setSkuName(skuInfo.getSkuName());
        cartItem.setIsChecked(1);
        cartItem.setCreateTime(new Date());
        cartItem.setUpdateTime(new Date());
        cartItem.setSkuPrice(skuInfo.getPrice());


        return cartItem;
    }
}
